/*
 * @Author: qianlihaoyue
 * @Date: 2023-06-21 20:04
 * @LastEditTime: 2023-06-21 21:09
 * @Description: 代码运行时间分析工具
 * @Version: V2.0
 *
 */
#pragma once
#include <chrono>
#include <cmath>
#include <iomanip>

#include <sstream>
#include <fstream>
#include <iostream>

#include <vector>
#include <map>
#include <unordered_map>

 /*
 当并行运算时，加权时间不对，以外圈的时间为准
 Timer::evaluate("build_add", [&, this]() {  });

 Timer::Evaluate("Run1", [&](){
     Timer::Start("Run1_d");
     Run1();
     Timer::End("Run1_d");
 });

 Timer::DumpIntoFile(std::string(ROOT_DIR)+"Log/time.txt",true);
 */

class Timer {
public:
    struct TimeRecorder {
        TimeRecorder() = default;
        TimeRecorder(const std::string& name, double time_usage) {
            func_name_ = name;
            time_usage_ms_.emplace_back(time_usage);
        }
        std::string func_name_;
        std::vector<double> time_usage_ms_;
    };

    static std::map<std::string, TimeRecorder> records_;
    static std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> start_records_;

    // 分离式设计 
    static void Start(const std::string& func_name) {
        bool isNoExist = (start_records_.find(func_name) == start_records_.end());
        start_records_[func_name] = std::chrono::high_resolution_clock::now();
    }

    static void End(const std::string& func_name, bool realtime_log = false) {
        if (start_records_.find(func_name) == start_records_.end()) {
            std::cerr << "There is no start before calling end !" << std::endl;
            return;
        }
        auto t2 = std::chrono::high_resolution_clock::now();
        double time_used = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - start_records_[func_name]).count() * 1000;

        if (records_.find(func_name) == records_.end())
            records_.insert({ func_name, TimeRecorder(func_name, time_used) });
        else
            records_[func_name].time_usage_ms_.emplace_back(time_used);
        if (realtime_log) std::cout << func_name << "\t: " << time_used << "ms" << std::endl;
    }

    // 包含待测函数
    template <class F>
    static void Evaluate(const std::string& func_name, F&& func, bool realtime_log = false) {
        double time_used = 0.0;
        auto t1 = std::chrono::high_resolution_clock::now();
        std::forward<F>(func)();
        auto t2 = std::chrono::high_resolution_clock::now();
        time_used = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1).count() * 1000;

        if (records_.find(func_name) == records_.end())
            records_.insert({ func_name, TimeRecorder(func_name, time_used) });
        else
            records_[func_name].time_usage_ms_.emplace_back(time_used);
        if (realtime_log) std::cout << func_name << "\t: " << time_used << "ms" << std::endl;
    }

    static void DumpIntoFile(const std::string& file_name, bool print_flag = false) {
        // 创建一个字符串流
        std::stringstream ss;
        std::ofstream ofs(file_name, std::ios::out);

        if (!ofs.is_open()) {
            std::cerr << "Failed to open file: " << file_name << "!!!" << std::endl;
            return;
        }
        else {
            std::cout << "Dump Time Records into file: " << file_name << std::endl;
        }

        ss << ">>> ===== Printing run time =====" << std::endl;
        if (print_flag) std::cout << ss.str();
        ofs << ss.str();    ss.str("");

        // 设置列宽
        const int nameWidth = 40;
        const int scoreWidth = 15;

        // 输出表头
        ss << std::left << std::setw(nameWidth) << "Function"
            << std::setw(scoreWidth) << "Ave time"      //平均运行时间
            << std::setw(scoreWidth) << "S.D time"      //运行时间标准差
            << std::setw(scoreWidth) << "Called times"  //调用次数
            << std::setw(scoreWidth) << "Normal time"   //平均时间*相对主程序调用倍数->求得的时间
            // << std::setw(scoreWidth) << "Real time"     //考虑并行加速的真实物理时间，没有必要，计算外圈时间就行
            << std::endl;
        if (print_flag) std::cout << ss.str();
        ofs << ss.str();    ss.str("");

        // 输出分隔线
        ss << std::setfill('-') << std::setw(nameWidth + 4 * scoreWidth) << "" << std::setfill(' ') << std::endl;
        if (print_flag) std::cout << ss.str();
        ofs << ss.str();    ss.str("");

        //获得最小计数，一般为主程序运行次数
        int min_count = 0;
        for (auto& r : records_) {
            if (!min_count) min_count = r.second.time_usage_ms_.size();
            if (r.second.time_usage_ms_.size() < min_count)
                min_count = r.second.time_usage_ms_.size();
        }
        //遍历计算每一项
        // map遍历顺序
        for (auto& r : records_) {
            std::vector<double>& tim = r.second.time_usage_ms_;
            //时间均值
            double ave_time = std::accumulate(tim.begin(), tim.end(), 0.0) / double(tim.size());

            // 计算方差
            double variance = 0.0;
            for (const auto& num : tim)
                variance += (num - ave_time) * (num - ave_time);
            variance /= tim.size();

            ss << std::fixed << std::setprecision(3) << std::left
                << std::setw(nameWidth) << r.first
                << std::setw(scoreWidth) << ave_time
                << std::setw(scoreWidth) << sqrt(variance)
                << std::setw(scoreWidth) << tim.size()
                << std::setw(scoreWidth) << ave_time * double(tim.size()) / double(min_count)
                << std::endl;
            if (print_flag) std::cout << ss.str();
            ofs << ss.str();    ss.str("");
        }
        ss << ">>> ===== Printing run time end =====" << std::endl;
        if (print_flag) std::cout << ss.str();
        ofs << ss.str();    ss.str("");

        // 输出所有数据
        size_t max_length = 0;
        for (const auto& kv : records_) {
            ofs << std::setw(scoreWidth) << kv.first;
            max_length = std::max(max_length, kv.second.time_usage_ms_.size());
        }
        ofs << std::endl;

        for (size_t i = 0; i < max_length; ++i) {
            for (const auto& kv : records_) {
                const auto& iter = kv.second;
                if (i < iter.time_usage_ms_.size())
                    ofs << std::setw(scoreWidth) << iter.time_usage_ms_[i];
                else
                    ofs << std::setw(scoreWidth) << "-";
                ofs << std::setw(scoreWidth);
            }
            ofs << std::endl;
        }
        ofs.close();
    }

    static void Clear() { records_.clear(); start_records_.clear(); }
};

// std::map 有序，且是根据键值进行排序的，unordered_map 完全无序
std::map<std::string, Timer::TimeRecorder> Timer::records_;
std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> Timer::start_records_;
